#include <Kharlia/Engine/Common.h>
#include <Kharlia/Engine/Actor.h>
#include <Kharlia/Engine/World.h>
#include <Kharlia/Engine/Engine.h>
#include <Kharlia/Engine/Client.h>
#include <Kharlia/Engine/Init.h>
#include <Kharlia/Core/ObjectUtil.h>

#define KH_ADD_PROPERTY_COPY_CONST_REF(name, getter, setter) \
    add_property(name, make_function(getter, return_value_policy<copy_const_reference>()), setter)

#define ENABLE_ALLOC_DEBUG 0

namespace kharlia { namespace engine {

// -------------------------------------------------
// AComponent
// -------------------------------------------------

AComponent::AComponent(PyObject* self):
    CObject(self),
    mNoDelete(false),
    mPriority(0),
    mNetDirty(false),
    mNetInitial(true),
    mNetOwner(false),
    mNetID(0),
    mNetTestRelevancy(false)
{
    if (ENABLE_ALLOC_DEBUG) {
        std::clog << "Component construct " << std::hex << (uint32)this << std::dec << "\n";
    }
}

AComponent::~AComponent() {
    if (ENABLE_ALLOC_DEBUG) {
        std::clog << "Component destruct " << std::hex << (uint32)this << std::dec << "\n";
    }
}

void AComponent::EnsureComponent(
    TPtr<AComponent>& ref,
    std::type_info const& reftype,
    bool create
    )
{
    if (!ref) {
        str ownerName(mActor);
        format msg("component is missing dependency: required_type=%1%, this_type=%2%, actor=%3%");
        msg % reftype.name();
        msg % typeid(*this).name();
        msg % extract<char*>(ownerName);
        throw RuntimeError(msg);
    }
}

void AComponent::InitImpl(TPtr<CActor> owner) {
    assert(!GetInited());

    mActor = owner;
    assert(mActor);
    mLevel = owner->GetLevel();

    TPtr<ANetwork> net = owner->GetNetwork();
    if (net && net.GetPtr() != this)
        net->InitedComponent(GetSelfPtr<AComponent>());
}

void AComponent::ExitImpl() {
    assert(GetInited());
    if (mNoDelete && GetLevel()->GetWorld()->GetGameStarted())
        throw RuntimeError("attempted to delete no-delete component during game");

    TPtr<ANetwork> net = mActor.Lock()->GetNetwork();
    if (net && net.GetPtr() != this)
        net->ExitedComponent(GetSelfPtr<AComponent>());

    mLevel.Reset();
    mActor.Reset();
    mNetID = 0;
}

void AComponent::Tick(float deltaTime) {
    assert(GetInited());
}

void AComponent::ResetLevel() {
    if (mActor)
        mLevel = mActor.Lock()->GetLevel();
    else
        mLevel.Reset();
}

KH_BEGIN_GC(AComponent)
    KH_GC_TPTR(thisx->mActor)
    KH_GC_TPTR(thisx->mLevel)
KH_END_GC()

// -------------------------------------------------
// ATransform
// -------------------------------------------------

bool ATransform::Move(FVector3F const& delta, bool smooth) {
    if (!GetLevel())
        throw RuntimeError("no level, not initialized");
    return GetLevel()->MoveActor(GetActor(), delta, smooth);
}

// -------------------------------------------------
// AMovement
// -------------------------------------------------

void AMovement::InitImpl(TPtr<CActor> owner) {
    AComponent::InitImpl(owner);
    SetPriority(1);
}

void AMovement::ExitImpl() {
    AComponent::ExitImpl();
}

void AMovement::Tick(float deltaTime) {
    AComponent::Tick(deltaTime);
    if (!mVelocity.IsZero()) {
        TPtr<ATransform> trans = GetActor()->GetTransform();
        //mTransform->AddPosition(mVelocity * deltaTime);
        if (trans)
            trans->Move(mVelocity * deltaTime);
    }
}

// -------------------------------------------------
// ARender
// -------------------------------------------------

ARender::ARender(PyObject* self):
    AComponent(self),
    mHidden(false)
{}

ARender::~ARender() {}

void ARender::InitImpl(TPtr<CActor> owner) {
    AComponent::InitImpl(owner);
    //mTransform = owner->GetTransform();
    //EnsureComponent(mTransform);
    SetPriority(1);
    //bool r = GetLevel()->AddRenderable(owner);
    //assert(r);
}

void ARender::ExitImpl() {
    //bool r = GetLevel()->RemoveRenderable(GetActor());
    //assert(r);
    //mTransform.reset();
    AComponent::ExitImpl();
}

void ARender::Tick(float deltaTime) {
    AComponent::Tick(deltaTime);
}

// -------------------------------------------------
// ASprite
// -------------------------------------------------

ASprite::ASprite(PyObject* self):
    ARender(self),
    mDirection(EDirection::SOUTH)
{
}

ASprite::~ASprite()
{
}

void ASprite::InitImpl(TPtr<CActor> owner) {
    ARender::InitImpl(owner);
}

void ASprite::ExitImpl() {
    ARender::ExitImpl();
}

void ASprite::Tick(float deltaTime) {
    ARender::Tick(deltaTime);
}

void ASprite::Draw(CClient* client) {
    if (!GetActor())
        return;
    FVector3F const& pos = GetActor()->GetTransform()->GetPosition();
    if (mImage && mImage->GetIsLoaded()) {
        client->DrawSprite(
            pos,
            mImage,
            FRectF(FVector2F(), mImage->GetSrcSize()),
            ui::colour(1.0f, 1.0f, 1.0f, 1.0f)
            );
    }
    else {
        client->DrawSprite(
            pos,
            client->TempActorTexture,
            FRectF(FVector2F(), client->TempActorTexture->getOriginalDataSize()),
            ui::colour(1.0f, 1.0f, 1.0f, 1.0f)
            );
    }
}

// -------------------------------------------------
// ASpriteAnimation
// -------------------------------------------------

ASpriteAnimation::ASpriteAnimation(PyObject* self):
    ASprite(self),
    mCurrentFrameList(0),
    mCurrentFrame(0),
    mCurrentTime(0.0f),
    mShouldMapToDirections(false)
{
}

ASpriteAnimation::~ASpriteAnimation() {
}

void ASpriteAnimation::InitImpl(TPtr<CActor> owner) {
    ASprite::InitImpl(owner);
}

void ASpriteAnimation::ExitImpl() {
    ASprite::ExitImpl();
}

void ASpriteAnimation::Tick(float deltaTime) {
    if (!IsDefinitionLoaded())
        return;

    if (mShouldMapToDirections) {
        // Update the sprite direction based on the velocity of the actor
        TPtr<AMovement> const& movem = GetActor()->GetMovement();
        if (movem) {
            FVector3F const& vel = movem->GetVelocity();
            if (abs(vel.X) > abs(vel.Y))
                SetDirection(vel.X > 0.0f ? EDirection::EAST : EDirection::WEST);
            else if (abs(vel.Y) != abs(vel.X))
                SetDirection(vel.Y > 0.0f ? EDirection::SOUTH : EDirection::NORTH);

        }
        // Select a frame list depending on the direction of the sprite
        uint16 newindex = GetDirectionFrameList(GetDirection());
        if (newindex != mCurrentFrameList)
            SetCurrentFrameList(newindex);
    }

    // Advance frame based on the time. Loop back to first frame if necessary or
    // remain on the last frame indefinitely.
    mCurrentTime += deltaTime;
    CSpriteAniDef::FrameList const& frames = mDefinition->GetFrameList(mCurrentFrameList);
    if (mCurrentTime >= frames.at(mCurrentFrame).Time) {
        mCurrentTime = 0.0f;
        if (mCurrentFrame == frames.size() - 1) {
            if (mDefinition->GetShouldLoop())
                mCurrentFrame = 0;
        }
        else
            mCurrentFrame += 1;
    }
}

void ASpriteAnimation::Draw(CClient* client) {
    if (!IsDefinitionLoaded()) {
        ASprite::Draw(client);
        return;
    }

    TPtr<ATransform> const& trans = GetActor()->GetTransform();
    if (!trans)
        return;

    CSpriteAniDef::Frame const& frame = mDefinition->GetFrame(mCurrentFrameList,
                                                              mCurrentFrame);
    client->DrawSprite(
        trans->GetPosition(),
        GetImage(),
        frame.Rect,
        ui::colour(1.0f, 1.0f, 1.0f, 1.0f));
}

uint16 ASpriteAnimation::GetDirectionFrameList(EDirection::Type dir) const {
    return (uint16)dir;
}

void ASpriteAnimation::SetCurrentFrameList(uint16 index) {
    if (!IsDefinitionLoaded())
        return;

    mDefinition->GetFrameList(index);
    mCurrentFrameList = index;
    SetCurrentFrame(0);
}

void ASpriteAnimation::SetCurrentFrame(uint16 index) {
    if (!IsDefinitionLoaded())
        return;
    
    mDefinition->GetFrame(mCurrentFrameList, index);
    mCurrentFrame = index;
}

void ASpriteAnimation::SetDefinition(TPtr<CSpriteAniDef> const& def) {
    mDefinition = def;
    mNetDirty = true;
    mCurrentFrameList = 0;
    mCurrentFrame = 0;
    mCurrentTime = 0.0f;
}

bool ASpriteAnimation::IsDefinitionLoaded() const {
    return mDefinition && mDefinition->GetIsLoaded();
}

// -------------------------------------------------
// ACollider
// -------------------------------------------------

ACollider::ACollider(PyObject* self):
    AComponent(self),
    mCollideTiles(false),
    mCollideActors(false),
    mBlockActors(false),
    mCollisionLayer(-1),
    mBounds(),
    mLastActorHitTime(0),
    mLastActorHit(nullptr) {}

void ACollider::InitImpl(TPtr<CActor> owner) {
    AComponent::InitImpl(owner);
    SetPriority(1);
    //GetLevel()->AddCollidable(GetSelfPtr<ACollider>());
}

void ACollider::ExitImpl() {
    // End touches
    TPtr<ACollider> self = GetSelfPtr<ACollider>();
    for (uint i = 0; i < mTouchers.size(); i++) {
        auto t = mTouchers[i].Lock();
        assert(t);
        t->EndTouch(self);
    }
    mTouchers.clear();

    //GetLevel()->RemoveCollidable(GetSelfPtr<ACollider>());
    AComponent::ExitImpl();
}

void ACollider::BeginTouchImpl(TPtr<ACollider> other) {
    AddToucher(other);
}

void ACollider::EndTouchImpl(TPtr<ACollider> other) {
    int index = GetToucherIndex(other);
    assert(index != -1);
    mTouchers.erase(mTouchers.begin() + index);
}

void ACollider::BumpImpl(TPtr<ACollider> other) {
}

// For unpickling
void ACollider::AddToucher(TPtr<ACollider> other) {
    mTouchers.push_back(TWeakPtr<ACollider>(other));
}

int ACollider::GetToucherIndex(TPtr<ACollider> other) const {
    for (uint i = 0; i < mTouchers.size(); i++) {
        auto t = mTouchers[i].Lock();
        assert(t);
        if (t == other) {
            return i;
        }
    }
    return -1;
}

void ACollider::EndTouches() {
    auto self = GetSelfPtr<ACollider>();
    for (int i = mTouchers.size() - 1; i >= 0; i--) {
        auto t = mTouchers[i].Lock();
        assert(t);
        EndTouch(t);
        t->EndTouch(self);
    }
    assert(mTouchers.size() == 0);
}

bool ACollider::IsTouching(TPtr<ACollider> other) const {
    return GetToucherIndex(other) != -1;
}

// -------------------------------------------------
// ACamera
// -------------------------------------------------

void ACamera::InitImpl(TPtr<CActor> owner) {
    AComponent::InitImpl(owner);
    mTarget = GetActor()->GetTransform();
    SetPriority(2);
}

void ACamera::ExitImpl() {
    mTarget.Reset();
    AComponent::ExitImpl();
}

KH_BEGIN_GC(ACamera)
    KH_GC_TPTR(thisx->mTarget)
KH_END_GC()

// -------------------------------------------------
// AController
// -------------------------------------------------

AController::AController(PyObject* self):
    AComponent(self)
{
}

void AController::InitImpl(TPtr<CActor> owner) {
    if (typeid(*this) == typeid(AController))
        throw RuntimeError("AController is abstract");
    AComponent::InitImpl(owner);
    SetPriority(10);
}

void AController::ExitImpl() {
    AComponent::ExitImpl();
}

void AController::Tick(float deltaTime) {
    AComponent::Tick(deltaTime);
}

#define GF(name) static FFieldInfo const* f##name = GetFieldInfo(fields, #name)

void AController::OnReplicated(FFieldInfo const* field, FFieldInfo const** fields) {
    GF(mPawn);

    if (field == fmPawn) {
        //std::cout << "Replicated pawn\n";
    }
}

KH_BEGIN_GC(AController)
    KH_GC_TPTR(thisx->mPawn)
KH_END_GC()

// -------------------------------------------------
// APlayerController
// -------------------------------------------------

APlayerController::APlayerController(PyObject* self):
    AController(self)
{
}

void APlayerController::InitImpl(TPtr<CActor> owner) {
    AController::InitImpl(owner);
}

void APlayerController::ExitImpl() {
    AComponent::ExitImpl();
}

void APlayerController::TickImpl(float deltaTime) {
    AController::Tick(deltaTime);
}

void APlayerController::Tick(float deltaTime) {
    call_method<void>(mSelf, "Tick", deltaTime);
}

KH_BEGIN_GC(APlayerController)
    KH_GC_TPTR(thisx->mPlayer)
    KH_GC_TPTR(thisx->mCamera)
KH_END_GC()

// -------------------------------------------------
// APawn
// -------------------------------------------------

APawn::APawn(PyObject* self):
    AComponent(self)
{
}

void APawn::InitImpl(TPtr<CActor> owner) {
    AComponent::InitImpl(owner);
    SetPriority(5);
}

void APawn::ExitImpl() {
    AComponent::ExitImpl();
}

void APawn::Tick(float deltaTime) {
    AComponent::Tick(deltaTime);
}

KH_BEGIN_GC(APawn)
    KH_GC_TPTR(thisx->mController)
KH_END_GC()

// -------------------------------------------------
// CActor
// -------------------------------------------------

handle<PyTypeObject> CActor::msComponentSlotTypes[SLOT_LAST];

CActor::CActor(PyObject* self):
    CObject(self),
    mID(0),
    mComponents(),
    mRole(ENetRole::NONE),
    mRemoteRole(ENetRole::NONE),
    mAlwaysRelevant(false),
    mOnlyRelevantToOwner(false)
{
    if (ENABLE_ALLOC_DEBUG) {
        std::clog << "Actor construct " << std::hex << (uint32)this << std::dec << "\n";
    }
}

CActor::~CActor() {
    if (ENABLE_ALLOC_DEBUG) {
        std::clog << "Actor destruct " << std::hex << (uint32)this << std::dec << "\n";
    }
}

void CActor::SetOwner(TPtr<CActor> const& other) {
    TPtr<CActor> current = mOwner.Lock();
    if (current) {
        current->LostChild(GetSelfPtr<CActor>());
    }
    mOwner = other;
    if (other) {
        assert(mOwner.Lock());
        other->GainedChild(GetSelfPtr<CActor>());
    }
}

void CActor::SetChildOwners(TPtr<CActor> const& actor) {
    for (int i = mChildren.size() - 1; i >= 0; i--) {
        TPtr<CActor> child = mChildren[i].Lock();
        assert(child);
        child->SetOwner(actor);
    }
}

void CActor::GainedChild(TPtr<CActor> const& actor) {
    mChildren.push_back(TWeakPtr<CActor>(actor));
}

void CActor::LostChild(TPtr<CActor> const& actor) {
    auto it = std::find(mChildren.begin(), mChildren.end(), actor);
    assert(it != mChildren.end());
    mChildren.erase(it);
}

void CActor::Tick(float deltaTime) {
    for (uint i = 0; i < mComponents.size(); i++) {
        mComponents[i].Component->Tick(deltaTime);
    }
}

TPtr<AComponent> CActor::GetComponent(object cls) const {
    if (PyNumber_Check(cls.ptr())) {
        handle<> num(PyNumber_Int(cls.ptr()));
        return GetComponent(PyInt_AsLong(num.get()));
    }
    if (!PyType_Check(cls.ptr()))
        throw TypeError("cls must be instance of type");
    for (uint i = 0; i < mComponents.size(); i++) {
        FComponentEntry const& c = mComponents[i];
        if (PyObject_IsInstance(c.Component->GetSelf().ptr(), cls.ptr())) {
            return c.Component;
        }
    }
    return TPtr<AComponent>();
}

TPtr<AComponent> CActor::GetComponent(py::type_info info) const {
    return GetComponent(core::GetTypeObject(info));
}

void CActor::AddComponent(TPtr<AComponent> const& comp) {
    assert(comp);
    if (comp->GetInited()) {
        throw RuntimeError("component already has an owner");
    }

    FComponentEntry entry(comp);
    int slotIndex = -1;

    // If there is no owner it shouldn't already be in the list
    assert(std::find(mComponents.begin(), mComponents.end(), entry) == mComponents.end());

    // Find a slot for the component
    for (uint i = 0; i < SLOT_LAST; i++) {
        if (PyType_IsSubtype(comp->GetTypeHandle().get(), msComponentSlotTypes[i].get()) &&
                !mComponentSlots[i]) {
            slotIndex = i;
            break;
        }
    }

    // An exception thrown in Init should not leave the actor
    // in a bad state
    TPtr<CActor> self = GetSelfPtr<CActor>();
    comp->Init(self);
    assert(comp->GetActor() == self);

    //try {
        // Insert into slot
        if (slotIndex != -1)
            mComponentSlots[slotIndex] = comp;
        // Insert into vector
        if (mComponents.size() == 0)
            mComponents.push_back(entry);
        else {
            sint16 priority = comp->GetPriority();
            // Keep the vector sorted by priority
            for (int i = mComponents.size() - 1; i >= 0; i--) {
                if (mComponents[i].Component->GetPriority() <= priority) {
                    mComponents.insert(mComponents.begin() + i + 1, entry);
                    break;
                }
            }
        }

        if (GetLevel()->GetGameStarted())
            comp->OnGameStarted();
    //}
    //catch (...) {
    //    comp->Exit();
    //    throw;
    //}
}

void CActor::RemoveComponent(TPtr<AComponent> const& comp) {
    assert(comp);
    if (comp->GetActor().GetPtr() != this)
        return;
    assert(comp->GetInited());

    if (GetLevel()->GetGameStarted())
        comp->OnGameStopped();

    comp->Exit();

    for (uint i = 0; i < SLOT_LAST; i++) {
        if (mComponentSlots[i] == comp) {
            mComponentSlots[i].Reset();
            break;
        }
    }

    FComponentEntry entry(comp);
    ComponentsType::iterator it = std::find(mComponents.begin(), mComponents.end(), entry);
    if (it == mComponents.end())
        throw RuntimeError("owned component not found in list");
    mComponents.erase(it);
}

void CActor::SetLevel(TPtr<CLevel> const& level) {
    mLevel = level;
    for (uint i = 0; i < mComponents.size(); i++) {
        mComponents[i].Component->ResetLevel();
    }
}

void CActor::ClearComponents() {
    //object printfunc = GetInnerModuleAttr("PrintObjectGC");
    ////PyObject* lastcomp;
    //std::cout << "BEGIN REMOVE COMPONENTS\n";
    //printfunc(GetSelf());
    for (uint i = mComponents.size() - 1; mComponents.size() > 0; i--) {
        TPtr<AComponent> comp = mComponents[i].Component;
        RemoveComponent(comp);
        //lastcomp = comp.GetHandle().get();
        //printfunc(comp);
    }
    //printfunc(GetSelf());
    //std::cout << "END REMOVE COMPONENTS\n";
}

void CActor::Init(uint32 id, object const& name, TPtr<CLevel> const& level, TPtr<CActor> const& owner) {
    if (mID)
        throw RuntimeError("already initialized");
    mID = id;
    SetName(name);
    SetLevel(level);
    SetOwner(owner);
}

void CActor::Exit() {
    ClearComponents();

    SetChildOwners(GetOwner());

    SetOwner(TPtr<CActor>());
    SetLevel(TPtr<CLevel>());
    mID = 0;
}

void CActor::SetName(object const& name) {
    mName = name;
}

bool CActor::Rename(object const& newname) {
    return GetLevel()->RenameActor(GetSelfPtr<CActor>(), newname);
}

KH_BEGIN_GC(CActor)
    KH_GC_TPTR(thisx->mLevel)
    KH_GC_TPTR(thisx->mOwner)
    for (uint i = 0; i < thisx->mChildren.size(); i++) {
        KH_GC_TPTR(thisx->mChildren[i])
    }
    for (uint i = 0; i < thisx->mComponents.size(); i++) {
        KH_GC_TPTR(thisx->mComponents[i].Component)
        KH_GC_HANDLE_EX(thisx->mComponents[i].Type, PyTypeObject)
    }
    for (uint i = 0; i < SLOT_LAST; i++) {
        KH_GC_TPTR(thisx->mComponentSlots[i]);
    }
KH_END_GC()

tuple CActor::Pickler::getstate(CActor& actor) {
    const int VERSION = 3;
    // 2 - Add name
    // 3 - Add children
    // Get instance dict
    object idict;
    //objects::instance<>* inst = downcast<objects::instance<>>(actor.mSelf);
    //if (inst->dict)
    //    idict = object(handle<>(borrowed(inst->dict)));

    // Get components
    list components;
    for (uint i = 0; i < actor.mComponents.size(); i++) {
        components.append(actor.mComponents[i].Component);
    }
    list slots;
    for (uint i = 0; i < SLOT_LAST; i++) {
        slots.append(actor.mComponentSlots[i]);
    }
    list children;
    for (uint i = 0; i < actor.mChildren.size(); i++) {
        children.append(actor.mChildren[i].Lock());
    }
    return py::make_tuple(
        VERSION,
        idict,
        components,
        slots,
        actor.GetOwner(),
        actor.mTag,
        actor.GetLevel(),
        actor.mID,
        actor.mName,
        children
        );
}

void CActor::Pickler::setstate(CActor& actor, tuple state) {
    int version              = extract<int>(state[0]);
    object idict             = state[1];
    list components          = extract<list>(state[2]);
    list slots               = extract<list>(state[3]);
    actor.mOwner             = extract<TPtr<CActor>>(state[4]);
    actor.mTag               = extract<unicode>(state[5]);
    actor.mLevel             = extract<TPtr<CLevel>>(state[6]);
    actor.mID                = extract<uint32>(state[7]);
    if (version >= 2)
        actor.mName = state[8];

    if (!idict.is_none()) {
        assert(PyDict_Check(idict.ptr()));
        objects::instance<>* inst = downcast<objects::instance<>>(actor.mSelf);
        if (inst->dict)
            PyDict_Update(inst->dict, idict.ptr());
        else
            inst->dict = incref(idict.ptr());
    }

    if (len(components) > 0) {
        PY_FOR(c, components) {
            actor.mComponents.push_back(FComponentEntry(extract<TPtr<AComponent>>(c)));
        }
    }

    assert(len(slots) <= SLOT_LAST);
    for (int i = 0; i < len(slots); i++) {
        actor.mComponentSlots[i] = extract<TPtr<AComponent>>(slots[i]);
    }

    if (version >= 3) {
        list children = extract<list>(state[9]);
        PY_FOR(c, children) {
            TPtr<CActor> cp = extract<TPtr<CActor>>(c);
            actor.mChildren.push_back(TWeakPtr<CActor>(cp));
        }
    }
}

//struct FMemberWrapper {
//    std::type_info const& ClassType;
//    std::type_info const& DataType;
//
//    FMemberWrapper(std::type_info const& ct, std::type_info const& dt):
//        ClassType(ct),
//        DataType(dt) {}
//
//    virtual void* Get(void* obj) const = 0;
//    virtual void Set(void* obj, void* value) const = 0;
//};
//
//template <class T, class D>
//struct FMemberWrapperImpl: public FMemberWrapper {
//    D T::* MemberPtr;
//
//    FMemberWrapperImpl(D T::* mp):
//        FMemberWrapper(typeid(T), typeid(D)),
//        MemberPtr(mp) {}
//
//    virtual void* Get(void* obj) const {
//        return (void*)&(((T*)obj)->*MemberPtr);
//    }
//    
//    virtual void Set(void* obj, void* value) const {
//        (*((T*)obj)).*MemberPtr = *((D*)value);
//    }
//};
//
//template <class T, class D>
//shared_ptr<FMemberWrapper> MakeNewWrapper(D T::* mp) {
//    return shared_ptr<FMemberWrapper>(new FMemberWrapperImpl<T, D>(mp));
//}
//
//void _MemberTest(TPtr<ATransform> t) {
//    shared_ptr<FMemberWrapper> mg = MakeNewWrapper(&ATransform::mPosition);
//
//    assert(typeid(ATransform) == mg->ClassType);
//    assert(typeid(FVector3F) == mg->DataType);
//
//    FVector3F* pos = (FVector3F*)mg->Get(t.GetPtr());
//
//    assert(*pos == t->mPosition);
//    
//    FVector3F npos(3.5f, 6.11f, 0.0f);
//    mg->Set(t.GetPtr(), &npos);
//
//    assert(npos == t->mPosition);
//}

void _InitActorClasses() {
    //def("MemberTest", &_MemberTest);

    _InitActorBindings();

#define INIT_SLOT(TYPE) \
    { \
        object cls = GetTypeObject(type_id<A##TYPE>()); \
        CActor::msComponentSlotTypes[SLOT_##TYPE] = \
            handle<PyTypeObject>(borrowed((PyTypeObject*)cls.ptr())); \
    }

    INIT_SLOT(Transform)
    INIT_SLOT(Movement)
    INIT_SLOT(Render)
    INIT_SLOT(Collider)
    INIT_SLOT(Camera)
    INIT_SLOT(Pawn)
    INIT_SLOT(PlayerController)
    INIT_SLOT(Network)
}

void _ExitActorClasses() {
    for (int i = 0; i < SLOT_LAST; i++) {
        CActor::msComponentSlotTypes[i].reset();
    }
}

}}